<template>
  <a-form
    class="dy-form"
    layout="vertical"
    :hide-required-mark="isCheck"
    :class="isCheck ? 'check-detail-form' : ''"
    :scroll-to-first-error="true"
  >
    <div :class="isFlexRow ? 'md:flex md:flex-row md:flex-wrap md:-mr-6' : ''">
      <template v-for="el in formItemData" :key="el.key">
        <div v-if="el.subTitle" class="w-full mb-4.5 text-lg text-color-header font-semibold">
          {{ el.subTitle }}
        </div>
        <a-form-item
          v-else-if="!el.hide"
          :class="
            isFlexRow
              ? is3Col
                ? 'md:w-1/2 lg:w-1/3 md:!pr-6'
                : el.holdRow
                ? 'md:w-full md:!pr-6'
                : 'md:w-1/2 md:!pr-6'
              : ''
          "
          v-bind="!disabled && validateInfos && !isCheck ? validateInfos[el.key] : null"
        >
          <template v-if="el.label || (el.checkLabel && isCheck)" #label>
            <span class="whitespace-nowrap">
              {{ isCheck ? el.checkLabel || el.label : el.label }}
            </span>
            <span v-if="curAction !== CURDAction.CHECK && getLabelDes(el)" class="ml-2.5 text-xs text-color-secondary">
              {{ getLabelDes(el) }}
            </span>
          </template>
          <span v-if="isCheck" class="ant-form-text">{{ getCheckText(el, model) }}</span>
          <AInput v-else-if="el.loading" :disabled="true" :placeholder="t('common.loading')"></AInput>
          <!--3.0.0-beta.8 a-cascader 不能用component动态渲染-->
          <a-cascader
            v-else-if="el.type === FormItemComponentType.CASCADER"
            :disabled="disabled || (el.props && el.props.disabled) || (disableKeys && disableKeys.includes(el.key))"
            v-bind="(el.props as any) || {}"
            :value="model[el.key]"
            @change="
              (e) => {
                updateModelOnChange(e, el);
              }
            "
          />
          <!--3.0.0-beta.8 a-tree-select用component渲染会有如下警告 warning.js:6 Warning: `children` of TreeSelect is deprecated. Please use `treeData` instead. -->
          <a-tree-select
            v-else-if="el.type === FormItemComponentType.TREESELECT"
            v-bind="(el.props as any) || {}"
            v-model:tree-expanded-keys="(el.props as any).treeExpandedKeys"
            :disabled="disabled || (el.props && el.props.disabled) || (disableKeys && disableKeys.includes(el.key))"
            :value="model[el.key]"
            @change="
              (e) => {
                updateModelOnChange(e, el);
              }
            "
          />
          <component
            :is="getFormItemComponent(el)"
            v-else
            :model="model"
            :value="el.type === FormItemComponentType.SWITCH ? undefined : model[el.key]"
            :checked="model[el.key]"
            :disabled="disabled || (el.props && el.props.disabled) || (disableKeys && disableKeys.includes(el.key))"
            v-bind="el.props || {}"
            @change="
              (e) => {
                updateModelOnChange(e, el);
              }
            "
            @before-upload="$emit('beforeUpload')"
            @upload="$emit('upload')"
          >
            {{ el.text || "" }}
          </component>
          <component
            :is="typeof el.extraComponent == 'function' ? el.extraComponent({ curAction }) : el.extraComponent"
            v-if="el.extraComponent"
          />
        </a-form-item>
      </template>
    </div>
  </a-form>
</template>
<script lang="ts" setup>
import { TableColumnProps } from "ant-design-vue";
import { CURDAction, FormItemComponentType, isSelectComponent } from "/@/components/CURDDialog/models";
import type { DynamicFormExpose, FormItemData, ModelRules } from "/@/components/CURDDialog/models";
import type { TableRecord } from "/@/api/models";
import type { FormRules, FormValidateHandler, SelectOptionItem } from "/@/types/antd";
import { useI18n } from "/@/locales/useI18n";
import { computed, onMounted, reactive, ref, shallowRef, watch } from "vue";
import {
  Input as AInput,
  Radio,
  Checkbox as ACheckbox,
  DatePicker as ADatePicker,
  Select as ASelect,
  Cascader as ACascader,
  TreeSelect as ATreeSelect,
  Switch as ASwitch,
  InputNumber as AInputNumber,
  TimePicker as ATimePicker,
  RadioGroup as ARadioGroup,
  Form,
} from "ant-design-vue";
import ImgUpload from "/@/components/common/ImgUpload.vue";
import { getRules } from "/@/utils/verify";

const props = withDefaults(
  defineProps<{
    forceFlexRow?: boolean;
    forceColumn?: boolean;
    columns?: TableColumnProps<any>[];
    model: TableRecord;
    modelDateFields?: string[];
    modelFormSetting?: FormItemData[];
    modelRules?: ModelRules;
    onBeforeResetFields?: (data: FormItemData[]) => TableRecord;
    idKey?: string;
    curAction?: CURDAction;
    disabled?: boolean;
    disableKeys?: string[] | null;
    is3Col?: boolean;
  }>(),
  {
    curAction: CURDAction.CREATE,
  }
);
const emits = defineEmits(["beforeUpload", "upload", "init"]);
const { t } = useI18n();
// const isCreate = computed(() => props.curAction === CURDAction.CREATE);
const isCheck = computed(() => props.curAction === CURDAction.CHECK);
const isFlexRow = computed(() => {
  return (
    ((formItemData.value.length > 4 && props.curAction === CURDAction.CREATE) || props.forceFlexRow) &&
    !props.forceColumn
  );
});

function getLabelDes(el: FormItemData) {
  if (el.labelDes) {
    return el.labelDes;
  }
  const isMax = el.props && el.props.max && el.props.max > 1;
  if (el.type === FormItemComponentType.IMGUPLOAD) {
    return t("components.uploadImg.uploadTips") + (isMax ? "，按住ctrl可多选" : "");
  }
  return "";
}

function toggleShow(el: FormItemData, index: number, isShow?: boolean) {
  if (isShow === undefined) {
    if (el.show === undefined) {
      el.show = true;
    }
    el.show = !el.show;
  } else {
    el.show = isShow;
  }
  for (let i = index + 1; i < formItemData.value.length; i++) {
    const item = formItemData.value[i];
    if (item.subTitle) {
      break;
    }
    item.show = el.show;
  }
}

function updateModelOnChange(e, el: FormItemData) {
  // console.log("=======>updateModelOnChange", e, el);
  let value;
  if (e && e.target) {
    value = e.target.value;
    if (value === undefined) {
      value = e.target.checked;
    }
  } else {
    value = e;
  }
  Object.assign(props.model, {
    [el.key]: value,
  });
  if (el.onChange) {
    el.onChange(e);
  }
}

/**
 * init form
 */
const formItemData = ref<FormItemData[]>([]);
const validate = shallowRef<FormValidateHandler>();
const validateInfos = ref<Record<string, any>>({});
const resetFields = ref<(newValues?: any) => void>();
let formRules = reactive<FormRules>({});

function initForm() {
  console.log("==========>dynamicForm init");
  let data: FormItemData[] = [];
  for (let key in props.model) {
    if (key === props.idKey) continue;
    const item: FormItemData = {
      label: "",
      key,
    };
    if (props.columns) {
      for (let i = 0; i < props.columns.length; i++) {
        if (props.columns[i].dataIndex === key) {
          item.label = props.columns[i].title as string;
          break;
        }
      }
    }
    data.push(item);
  }
  if (props.modelFormSetting) {
    const modelFormSetting = [...props.modelFormSetting];
    for (let j = 0; j < modelFormSetting.length; j++) {
      const item = modelFormSetting[j];
      if (item.subTitle) continue;
      if (!item.props) {
        item.props = {};
      }
      if (item.type === FormItemComponentType.DATEPICKER || item.type === FormItemComponentType.RANGEPICKER) {
        if (item.props.valueFormat === undefined) {
          item.props.valueFormat = t("common.dateTimeFormatStr");
        }
      }
      if (!item.props.placeholder && !item.customComponent) {
        if (
          item.type === FormItemComponentType.DATEPICKER ||
          item.type === FormItemComponentType.SELECT ||
          item.type === FormItemComponentType.TREESELECT ||
          item.type === FormItemComponentType.CASCADER
        ) {
          item.props.placeholder = "请选择" + (item.props.mode === "multiple" ? "(可多选)" : "");
        } else if (
          !item.type ||
          item.type === FormItemComponentType.TEXT ||
          item.type === FormItemComponentType.NUMBER ||
          item.type === FormItemComponentType.TEXTAREA
        ) {
          item.props.placeholder = "请输入";
        }
      }
      let isExist = false;
      for (let i = 0; i < data.length; i++) {
        if (item.key === data[i].key) {
          isExist = true;
          Object.assign(data[i], item);
          break;
        }
      }
      if (!isExist) {
        data.push(item);
      }
    }
    //处理subTitle
    modelFormSetting.forEach((el) => {
      if (el.subTitle && el.beforeKey) {
        const index = data.findIndex((item) => item.key === el.beforeKey);
        index !== -1 && data.splice(index, 0, el);
      }
    });
  }
  formItemData.value = data;
  //表单校验规则
  const rules: FormRules = {};
  props.modelRules?.forEach((item) => {
    if (typeof item === "string") {
      const formItem = formItemData.value.find((el) => el.key === item);
      if (formItem) {
        rules[item] = getRules({
          required: true,
          pattern: formItem.pattern,
          patternKeys: formItem.patternKeys,
          messageName: isSelectComponent(formItem.type as FormItemComponentType) ? "" : formItem.label,
          selectMessageName: formItem.label,
          min: formItem.min,
          max: formItem.max,
          requiredMes: formItem.requiredMes,
        });
      } else {
        rules[item] = [
          {
            required: true,
            message: t("common.required"),
          },
        ];
      }
    } else if (typeof item === "object") {
      let key: string = "";
      for (let i in item) {
        key = i;
        break;
      }
      if (key) {
        let target = item[key];
        const formItem = formItemData.value.find((el) => el.key === key);
        if (formItem) {
          target = getRules({
            required: formItem.required,
            pattern: formItem.pattern,
            patternKeys: formItem.patternKeys,
            messageName: isSelectComponent(formItem.type as FormItemComponentType) ? "" : formItem.label,
            selectMessageName: formItem.label,
            min: formItem.min,
            max: formItem.max,
            requiredMes: formItem.requiredMes,
          }).concat(target);
          item[key] = target;
        }
        Object.assign(rules, item);
      }
    }
  });
  formItemData.value.forEach((item, i) => {
    if (
      (item.pattern || (item.patternKeys && item.patternKeys.length > 0) || item.min || item.max || item.required) &&
      !rules[item.key]
    ) {
      rules[item.key] = getRules({
        pattern: item.pattern,
        patternKeys: item.patternKeys,
        messageName: isSelectComponent(item.type as FormItemComponentType) ? "" : item.label,
        selectMessageName: item.label,
        min: item.min,
        max: item.max,
        required: item.required,
        requiredMes: item.requiredMes,
      });
    }
    if (item.subTitle && item.show === false) {
      toggleShow(item, i, false);
    }
  });
  formRules = reactive(rules);
  const result = Form.useForm(props.model, formRules);
  validate.value = result.validate;
  validateInfos.value = result.validateInfos;
  resetFields.value = result.resetFields;
}
initForm();
watch([() => props.modelFormSetting, () => props.model], () => {
  console.log("===========>dyForm change");
  initForm();
  initFormItemComponentData();
});

function getFormItemComponent(el: FormItemData) {
  if (el.customComponent) {
    return el.customComponent;
  }
  if (!el.type || el.type === FormItemComponentType.TEXT) {
    return AInput;
  }
  if (el.type === FormItemComponentType.NUMBER) {
    return AInputNumber;
  }
  if (el.type === FormItemComponentType.TEXTAREA) {
    return AInput.TextArea;
  }
  if (el.type === FormItemComponentType.RADIO) {
    return ARadioGroup;
  }
  if (el.type === FormItemComponentType.SELECT) {
    // return el.props && el.props.options && el.props.options.length <= 3 && !el.props.mode ? ARadioGroup : ASelect;
    return ASelect;
  }
  if (el.type === FormItemComponentType.DATEPICKER) {
    return ADatePicker;
  }
  if (el.type === FormItemComponentType.CHECKBOX) {
    return ACheckbox;
  }
  if (el.type === FormItemComponentType.CASCADER) {
    return ACascader;
  }
  if (el.type === FormItemComponentType.RANGEPICKER) {
    return ADatePicker.RangePicker;
  }
  if (el.type === FormItemComponentType.IMGUPLOAD) {
    return ImgUpload;
  }
  if (el.type === FormItemComponentType.TREESELECT) {
    return ATreeSelect;
  }
  if (el.type === FormItemComponentType.PASSWORD) {
    return AInput.Password;
  }
  if (el.type === FormItemComponentType.SWITCH) {
    return ASwitch;
  }
  if (el.type === FormItemComponentType.TIMEPICKER) {
    return ATimePicker;
  }
  return AInput;
}
//初始化需要异步加载数据的组件，比如Select
async function initFormItemComponentData() {
  const tasks: Promise<boolean>[] = [];
  for (let i = 0; i < formItemData.value.length; i++) {
    const el = formItemData.value[i];
    if (el.props && el.getData) {
      const p = new Promise<boolean>((resolve) => {
        if (!el.getData) {
          resolve(true);
          return;
        }
        el.loading = true;
        el.getData({ action: props.curAction, item: el })
          .then((data) => {
            if (data && el.props) {
              if (el.type === FormItemComponentType.TREESELECT) {
                el.props.treeData = data;
              } else if (el.customComponent) {
                el.props.data = data;
              } else {
                el.props.options = data;
              }
            }
            resolve(true);
          })
          .finally(() => {
            el.loading = false;
          });
      });
      tasks.push(p);
    }
  }
  return Promise.all(tasks);
}
function getCheckText(el: FormItemData, model: TableRecord) {
  //el.text || model[el.key]
  let val: string | number = "";
  if (el.getText) {
    val = el.getText(model[el.key], model);
  } else {
    val = el.text || model[el.key];
  }
  if (val === null || val === undefined || val === "") {
    return "--";
  }
  return val;
}
const requiredFields: { [key: string]: number } = {};
onMounted(() => {
  emits("init");
});
const dyExpose: DynamicFormExpose = {
  getOptionItemByValue(forItemKey, optionValue) {
    const item = formItemData.value.find((el) => el.key === forItemKey);
    if (item && item.props) {
      const options = item.props.options as SelectOptionItem[];
      if (options && options.length > 0) {
        return options.find((el) => typeof el === "object" && el.value === optionValue);
      }
    }
  },
  reloadFormItemComponentData(formItemKey, params) {
    const item = formItemData.value.find((el) => el.key === formItemKey);
    if (item && item.getData) {
      item.loading = true;
      item
        .getData({ action: props.curAction, item, params })
        .then((data) => {
          if (data && item.props) {
            if (item.type === FormItemComponentType.TREESELECT) {
              item.props.treeData = data;
            } else {
              item.props.options = data;
            }
          }
        })
        .finally(() => {
          item.loading = false;
        });
    }
  },
  validate(names?, option?) {
    if (validate.value) {
      return validate.value(names, option);
    }
    return Promise.resolve();
  },
  resetFields() {
    if (resetFields.value) {
      let newValues: TableRecord | undefined;
      if (props.onBeforeResetFields) {
        newValues = props.onBeforeResetFields(formItemData.value);
      }
      resetFields.value(newValues);
    }
    for (let i = 0; i < formItemData.value.length; i++) {
      const el = formItemData.value[i];
      if (el.customComponentRef && el.customComponentRef.clear) {
        el.customComponentRef.clear();
      }
    }
  },
  getFormItemData() {
    return formItemData.value;
  },
  setFieldRquired(key: string, required = false) {
    const rules = formRules[key];
    if (rules) {
      rules.forEach((rule) => {
        rule.required = required;
      });
    }
    const validateInfo = validateInfos.value[key];
    if (validateInfo) {
      validateInfo.required = required;
    }
  },
  initFormItemComponentData,
  //隐藏或显示字段
  toggleField(key, hide) {
    const item = formItemData.value.find((el) => el.key === key);
    if (!item) {
      return;
    }
    if (hide) {
      //隐藏要去掉必填规则
      const validateInfo = validateInfos.value[key];
      if (validateInfo && validateInfo.required) {
        requiredFields[key] = 1;
      }
      dyExpose.setFieldRquired(key, false);
    } else {
      if (requiredFields[key]) {
        //还原必填规则
        dyExpose.setFieldRquired(key, true);
        delete requiredFields[key];
      }
    }
    item.hide = hide;
  },
};
defineExpose(dyExpose);
</script>
<style lang="less">
.dy-form {
  .ant-form-item-explain.ant-form-item-explain-error {
    //触发多个错误的时候 只显示第一个
    height: 24px;
    overflow: hidden;
  }
}
</style>
